package math;


public class Negation implements ArithmeticExpression {

  private ArithmeticExpression arg;

  public Negation(ArithmeticExpression arg) {
    this.arg = arg;
  }

  @Override
  public ArithmeticExpression derivate(Variable var) {
    return createNegation(arg.derivate(var));
  }

  public static ArithmeticExpression createNegation(ArithmeticExpression arg) {
    if (arg.isZero())
      return IntegerConstant.ZERO;
    return new Negation(arg);
  }

  @Override
  public boolean isOne() {
    return false;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Negation) {
      Negation neg2 = (Negation) obj;
      return neg2.arg.equals(arg);
    } else
      return false;
  }

  @Override
  public boolean isZero() {
    return arg.isZero();
  }

  @Override
  public int getPriority() {
    return 2;
  }

  @Override
  public ArithmeticExpression simplify() {
    ArithmeticExpression newArg = arg.simplify();
    if (newArg.isZero())
      return IntegerConstant.ZERO;
    if (newArg instanceof Negation){
      Negation neg2 = (Negation) newArg;
      return neg2.arg;
    }
    if (newArg instanceof IntegerConstant){
      IntegerConstant constant = (IntegerConstant) newArg;
      return constant.negate();
    }
    
    if (newArg == arg)
      return this;
    else
      return new Negation(newArg);
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append('-');
    if (arg.getPriority() > getPriority())
      sb.append('(');
    sb.append(arg.toString());
    if (arg.getPriority() > getPriority())
      sb.append(')');
    return sb.toString();
  }

  @Override
  public boolean matches(Expression expr) {
    if (expr instanceof Negation) {
      Negation tNegation = (Negation) expr;
      return arg.matches(tNegation.getArgument());
    } else
      return false;
  }

  @Override
  public int countMembers() {
    return 1 + arg.countMembers();
  }

  public ArithmeticExpression getArgument() {
    return arg;
  }
  
  @Override
  public int hashCode() {
    return ~arg.hashCode();
  }

  @Override
  public <R> R accept(ExpressionVisitor<R> visitor) {
    return visitor.visitNegation(this);
  }
}